﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using WPFCABShell.$safeprojectname$.Interface;
using WPFCABShell.$safeprojectname$.Interface.Services.Options;
using WPFCABShell.$safeprojectname$.Interface.Services.Settings;
using Kent.Boogaart.HelperTrinity;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.ObjectBuilder;

namespace WPFCABShell.$safeprojectname$.Options
{
	public sealed class OptionsPresenter : Presenter<IOptionsView>, INotifyPropertyChanged, IBuilderAware
	{
		private readonly WorkItem _workItem;
		private readonly IDictionary<IOptions, IOptionsPane> _optionsPanes;
		private readonly ISettingsService _settingsService;
		private IOptionsPane _currentPane;
		private const string _dataKey = "Data";

		public IOptionsPane CurrentPane
		{
			get
			{
				return _currentPane;
			}
			private set
			{
				if (_currentPane != value)
				{
					_currentPane = value;
					OnPropertyChanged("CurrentPane");
				}
			}
		}

		public event PropertyChangedEventHandler PropertyChanged;

		public OptionsPresenter([ServiceDependency(Required = true)] WorkItem workItem, [ServiceDependency(Required = true)] ISettingsService settingsService)
		{
			_workItem = workItem;
			_settingsService = settingsService;
			_optionsPanes = new Dictionary<IOptions, IOptionsPane>();
		}

		public void SelectOptions(IOptions options)
		{
			IOptionsPane pane = null;

			if (options != null)
			{
				if (!_optionsPanes.TryGetValue(options, out pane))
				{
					ExceptionHelper.ThrowIf(options.PaneType == null, "NoPaneType");
					ExceptionHelper.ThrowIf(!typeof(IOptionsPane).IsAssignableFrom(options.PaneType), "PaneTypeDoesntImplementRequiredInterface", typeof(IOptionsPane).Name);
					ExceptionHelper.ThrowIf(options.PaneType.GetConstructor(Type.EmptyTypes) == null, "PaneTypeMustHaveDefaultConstructor");
					pane = _workItem.Items.AddNew(options.PaneType) as IOptionsPane;
					_optionsPanes[options] = pane;
				}

				if (pane.Data == null)
				{
					AssignPaneData(options, pane);
				}
			}

			CurrentPane = pane;
		}

		public void ApplyChanges()
		{
			foreach (IOptions options in _optionsPanes.Keys)
			{
				IOptionsPane pane = _optionsPanes[options];
				string optionsSettingsKey = GetSettingsKeyForOptions(options);
				ISettings settings = _settingsService.GetSettings(optionsSettingsKey);
				settings[_dataKey] = pane.Data;
				//make sure we clone data next time
				pane.Data = null;
				pane.Closed(true);
			}
		}

		public void RevertChanges()
		{
			foreach (IOptions options in _optionsPanes.Keys)
			{
				IOptionsPane pane = _optionsPanes[options];
				//make sure we clone data next time
				pane.Data = null;
				pane.Closed(false);
			}
		}

		private void AssignPaneData(IOptions options, IOptionsPane pane)
		{
			Debug.Assert(options != null);
			Debug.Assert(pane != null);
			Debug.Assert(pane.Data == null);

			string optionsSettingsKey = GetSettingsKeyForOptions(options);
			ISettings settings = _settingsService.GetSettings(optionsSettingsKey);

			if (settings.Contains(_dataKey))
			{
				pane.Data = settings.GetClone(_dataKey);
			}

			if (pane.Data == null)
			{
				pane.Data = pane.CreateData();
			}
		}

		private string GetSettingsKeyForOptions(IOptions options)
		{
			Debug.Assert(options != null);
			return string.Concat(options.GetType().FullName);
		}

		private void OnPropertyChanged(string propertyName)
		{
			EventHelper.Raise(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
		}

		void IBuilderAware.OnBuiltUp(string id)
		{
		}

		void IBuilderAware.OnTearingDown()
		{
			foreach (IOptions options in _optionsPanes.Keys)
			{
				IOptionsPane pane = _optionsPanes[options];
				string optionsSettingsKey = GetSettingsKeyForOptions(options);
				ISettings settings = _settingsService.GetSettings(optionsSettingsKey);
				settings.Save();
			}
		}
	}
}
